﻿using System;
using Newtonsoft.Json.Linq;
using Microsoft.ClearScript.V8;
using TrinityAPI.Base.Tools;

namespace TrinityAPI.Base.Models
{
    public class ScriptEngine : IDisposable
    {
        private string _script;
        private JObject _assembly;
        private V8ScriptEngine _engine;

        public dynamic Script
        {
            get
            {
                return Engine.Script;
            }
        }

        public object Evaluate(string code)
        {
            return Engine.Evaluate(code);
        }

        public object Invoke(string funcName, params object[] args)
        {
            return Engine.Invoke(funcName, args);
        }

        public void Execute(string code)
        {
            Engine.Execute(code);
        }

        public V8ScriptEngine Engine
        {
            get
            {
                if (_engine == null)
                {
                    _engine = new V8ScriptEngine();
                    if (_assembly != null)
                    {
                        foreach (var item in _assembly)
                        {
                            var assemblies = _assembly.GetJsonObjectList<string>(item.Key);
                            if (assemblies != null && assemblies.Count > 0 && item.Key != "host")
                                _engine.AddHostObject(item.Key, new Microsoft.ClearScript.HostTypeCollection(assemblies.ToArray()));
                        }
                    }
                    _engine.AddHostObject("host", new Microsoft.ClearScript.ExtendedHostFunctions());
                    if (!string.IsNullOrWhiteSpace(_script))
                        _engine.Execute(_script);
                }
                return _engine;
            }
        }

        public ScriptEngine()
        {
        }

        public ScriptEngine(string script)
        {
            this._script = script;
        }

        public ScriptEngine(string script, JObject assembly) : this(script)
        {
            this._assembly = assembly;
        }

        public ScriptEngine NewInstance(string script)
        {
            return NewInstance(script, null);
        }

        public ScriptEngine NewInstance(string script, JObject assembly)
        {
            JObject assemblyForCreate = _assembly == null ? null : _assembly.DeepClone() as JObject;
            string scriptForCreate = !string.IsNullOrWhiteSpace(script) && !string.IsNullOrWhiteSpace(_script) ? string.Format("{0};{1}", _script, script) : !string.IsNullOrWhiteSpace(script) ? script : _script;
            var mergeSettings = new JsonMergeSettings()
            {
                MergeArrayHandling = MergeArrayHandling.Union,
                MergeNullValueHandling = MergeNullValueHandling.Ignore,
                PropertyNameComparison = StringComparison.OrdinalIgnoreCase
            };
            if (assembly != null)
            {
                if (assemblyForCreate == null)
                    assemblyForCreate = assembly;
                else
                    assemblyForCreate.Merge(assembly, mergeSettings);
            }

            return new ScriptEngine(scriptForCreate, assemblyForCreate);
        }

        public void Dispose()
        {
            if (_engine != null)
                _engine.Dispose();
        }
    }
}
